﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace crdmcgui
{
    [Serializable]
    public class RulesetCollection : DataManagerContainer
    {
        public KeyValuePair<string, string>[] Variables { get; set; }
        public RulesetGroup Residual { get; set; }
        public string Author { get; set; }
        public override string Name
        {
            get
            {
                return Author;
            }
            set
            {
                Author = value;
            }
        }
        public string Notes { get; set; }
        public override string Comment
        {
            get
            {
                return Notes;
            }
            set
            {
                Notes = value;
            }
        }


        public string TextVersion
        {
            get
            {
                return GetTextOuput();
            }
            set
            {
                Parse(value.Split(new string[] { "\r", "\n", }, StringSplitOptions.RemoveEmptyEntries));
            }
        }

        private string GetTextOuput()
        {
            string strReturn = "";

            //get the compile
            string[] tmp = Compile();

            for (int i = 0; i < tmp.Length; i++)
            {
                strReturn += tmp[i] + "\r\n";
            }

            return strReturn;
        }

        public RulesetCollection()
            : base()
        {
            Variables = new KeyValuePair<string, string>[0];
            Residual = new RulesetGroup();
        }

        public RulesetCollection(string strFilePath)
            : this()
        {
            if (System.IO.File.Exists(strFilePath))
            {
                string[] masterRules = System.IO.File.ReadAllLines(strFilePath);
                Parse(masterRules);
            }
        }

        /// <summary>
        /// Creates a Rulset collection from merging two Data Manager Rule files
        /// </summary>
        /// <param name="strFileOnePath">the filepath to the first file</param>
        /// <param name="strFileTwoPath">the filepath to the second file</param>
        public RulesetCollection(string strFileOnePath, string strFileTwoPath)
            : this(strFileOnePath)
        {
            string[] fileTwo = System.IO.File.ReadAllLines(strFileTwoPath);
            Merge(fileTwo);
        }

        public RulesetCollection(RulesetGroup rsGroup)
            :this()
        {
            this.Groups.Add(rsGroup.Clone());
        }

        private void SetVariable(string p, string p_2)
        {
            try
            {
                KeyValuePair<string, string> tmp = FindVariable(p);
                RemoveVariable(p);
            }
            catch { }

            AddVariable(p, p_2);
        }

        private void AddVariable(string p, string p_2)
        {
            KeyValuePair<string, string>[] tmpVariables = new KeyValuePair<string, string>[Variables.Length + 1];

            for (int i = 0; i < Variables.Length; i++)
            {
                tmpVariables[i] = Variables[i];
            }
            tmpVariables[Variables.Length] = new KeyValuePair<string, string>(p, p_2);
            Variables = tmpVariables;
        }

        private bool RemoveVariable(string p)
        {
            int Index = IndexOfVariable(p);

            if (Index > -1)
            {
                KeyValuePair<string, string>[] tmpVariables = new KeyValuePair<string, string>[Variables.Length - 1];

                for (int i = 0; i < Variables.Length; i++)
                {
                    if (i < Index)
                        tmpVariables[i] = Variables[i];
                    else if (i > Index)
                        tmpVariables[i - 1] = Variables[i];
                }
                return true;
            }
            return false;
        }

        private int IndexOfVariable(string p)
        {
            int nReturn = -1;
            for (int i = 0; i < Variables.Length; i++)
            {
                if (Variables[i].Key.ToLower() == p.ToLower())
                {
                    return i;
                }
            }
            return nReturn;
        }

        private KeyValuePair<string, string> FindVariable(string p)
        {
            throw new NotImplementedException();
        }

        public void Parse(string[] masterRules)
        {
            Reset();
            int i = 0;
            while (i < masterRules.Length && IdentifyLineType(masterRules[i]) != LineParseType.EndRulesLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(masterRules[i]))
                {
                    case LineParseType.AuthorLine:
                        this.Author = masterRules[i].Replace(Global.AuthorHeader, "");
                        break;
                    case LineParseType.GroupStartLine:
                        i = ParseGroup(masterRules, i, this);
                        break;
                    case LineParseType.SetVariableLine:
                        tmpVariable = masterRules[i].Replace(Global.VariableHeader, "").Trim().Split(new string[] { " " }, 2, StringSplitOptions.RemoveEmptyEntries);
                        if (tmpVariable.Length > 0)
                        {
                            if (tmpVariable.Length > 1)
                            {
                                SetVariable(tmpVariable[0], tmpVariable[1]);
                            }
                            else
                                SetVariable(tmpVariable[0], "");
                        }
                        break;
                    case LineParseType.NotesStartLine:
                        i = ParseNotes(masterRules, i);
                        break;
                    case LineParseType.RulesetNameLine:
                    case LineParseType.RulesetLine:
                        i = ParseRuleset(masterRules, i, this);
                        break;
                    default:
                        break;
                }
                i++;
            }

            i++;

            if (i < masterRules.Length)
            {
                ParseResidual(masterRules, i);
            }
        }

        private void Reset()
        {
            this.Author = "";
            this.Comment = "";
            this.Groups = new SortableBindingList<RulesetGroup>();
            this.Residual = new RulesetGroup();
            this.Rulesets = new SortableBindingList<Ruleset>();
            this.Variables = new KeyValuePair<string, string>[0];
        }

        public void Merge(string[] masterRules)
        {
            int i = 0;
            while (i < masterRules.Length && IdentifyLineType(masterRules[i]) != LineParseType.EndRulesLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(masterRules[i]))
                {
                    case LineParseType.AuthorLine:
                        this.Author = masterRules[i].Replace(Global.AuthorHeader, "");
                        break;
                    case LineParseType.GroupStartLine:
                        i = MergeGroup(masterRules, i, this);
                        break;
                    case LineParseType.SetVariableLine:
                        tmpVariable = masterRules[i].Replace(Global.VariableHeader, "").Trim().Split(new string[] { " " }, 2, StringSplitOptions.RemoveEmptyEntries);
                        if (tmpVariable.Length > 0)
                        {
                            if (tmpVariable.Length > 1)
                            {
                                SetVariable(tmpVariable[0], tmpVariable[1]);
                            }
                            else
                                SetVariable(tmpVariable[0], "");
                        }
                        break;
                    case LineParseType.NotesStartLine:
                        i = ParseNotes(masterRules, i);
                        break;
                    case LineParseType.RulesetNameLine:
                    case LineParseType.RulesetLine:
                        i = ParseRuleset(masterRules, i, this);
                        break;
                    default:
                        break;
                }
                i++;
            }

            i++;

            if (i < masterRules.Length)
            {
                ParseResidual(masterRules, i);
            }
        }

        public void Import(string[] masterRules)
        {
            int i = 0;
            while (i < masterRules.Length && IdentifyLineType(masterRules[i]) != LineParseType.EndRulesLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(masterRules[i]))
                {
                    case LineParseType.GroupStartLine:
                        i = ParseGroup(masterRules, i, this);
                        break;
                    case LineParseType.RulesetNameLine:
                    case LineParseType.RulesetLine:
                        i = ParseRuleset(masterRules, i, this);
                        break;
                    default:
                        break;
                }
                i++;
            }

            i++;

            if (i < masterRules.Length)
            {
                ParseResidual(masterRules, i);
            }
        }

        private int ParseNotes(string[] strNotesArray, int nStartFrom)
        {
            int nReturn = nStartFrom + 1;

            while (nReturn < strNotesArray.Length && IdentifyLineType(strNotesArray[nReturn]) != LineParseType.NotesEndLine)
            {
                switch (IdentifyLineType(strNotesArray[nReturn]))
                {
                    case LineParseType.NotesEndLine:
                        return nReturn;
                    default:
                        Notes += strNotesArray[nReturn].Replace("# ", "") + "\r\n";
                        break;
                }
                nReturn++;
            }
            return nReturn;
        }

        private int ParseResidual(string[] strResidualArray, int nStartFrom)
        {
            int i = nStartFrom;
            while (i < strResidualArray.Length && IdentifyLineType(strResidualArray[i]) != LineParseType.EndRulesLine)
            {
                string[] tmpVariable = new string[0];
                switch (IdentifyLineType(strResidualArray[i]))
                {
                    case LineParseType.GroupStartLine:
                        i = ParseGroup(strResidualArray, i, Residual);
                        break;
                    case LineParseType.RulesetNameLine:
                    case LineParseType.RulesetLine:
                        i = ParseRuleset(strResidualArray, i, Residual);
                        break;
                    default:
                        break;
                }
                i++;
            }
            return i;
        }

        public override string[] Compile()
        {
            List<string> Compiled = new List<string>(CreateHeader());
            //Write VARIABLES
            for (int i = 0; i < Variables.Length; i++)
            {
                Compiled.Add(Global.VariableHeader + Variables[i].Key + " " + Variables[i].Value);
            }

            //run base compile
            Compiled.AddRange(base.Compile());


            //END Rules
            Compiled.Add(Global.EndOfRules);
            //re add residual
            Compiled.AddRange(CreateResidual());
            return Compiled.ToArray();
        }

        private string[] CreateHeader()
        {
            List<string> strReturn = new List<string>(new string[] { "#Created by CR Data Manager GUI ", "#Do Not Edit Manually unless you know what you are doing", "#", "#", "#" });

            if (!string.IsNullOrEmpty(Author))
                strReturn.Add(Global.AuthorHeader + Author);
            if (!string.IsNullOrEmpty(Notes))
                strReturn.AddRange(CreateNotes());
            return strReturn.ToArray();
        }

        private string[] CreateResidual()
        {
            List<string> tmpList = new List<string>(Residual.Compile());

            //strip the headers and footers
            tmpList.RemoveAt(0);
            tmpList.RemoveAt(0);
            tmpList.RemoveAt(tmpList.Count - 1);
            tmpList.RemoveAt(tmpList.Count - 1);

            return tmpList.ToArray();
        }

        private string[] CreateNotes()
        {
            List<string> tmp = new List<string>(Notes.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None));

            tmp.Insert(0, "#");
            tmp.Insert(0, Global.NotesHeader);

            for (int i = 0; i < tmp.Count; i++)
            {
                tmp[i] = tmp[i].Insert(0, "# ");
            }

            tmp.Add(Global.EndOfNotes);

            return tmp.ToArray();
        }

    }

    public enum LineParseType
    {
        AuthorLine,
        BlankLine,
        RulesetLine,
        GroupStartLine,
        GroupEndLine,
        RulesetNameLine,
        SetVariableLine,
        DividerLine,
        Unknown,
        EndRulesLine,
        NotesEndLine,
        NotesStartLine
    }
}
